module canvas.abstractcanvas;

import utils.vector2;
import utils.color;
import utils.time;
import derelict.devil.il;
import std.string;
import std.stdio;

class AbstractCanvas {
    protected Vector2i mRes;

    ///time difference between rendering two frames
    protected Time mDeltaT;

    ///create new canvas with resolution res and initialize
    this(Vector2i res) {
        mRes = res;
    }

    ///called before drawing to the canvas (drawPixel)
    public abstract void enterRenderMode();

    ///called after drawing
    public abstract void leaveRenderMode();

    ///set pixel at pos to color col
    public abstract void drawPixel(Vector2i pos, RGBColor col);

    ///draw pixel, checking bounding box
    public void drawSafePixel(Vector2i pos, RGBColor col) {
        if (pos.x >= 0 && pos.x < mRes.x && pos.y >= 0 && pos.y < mRes.y) {
            drawPixel(pos, col);
        }
    }

    ///get color of pixel at pos
    public abstract void getPixelColor(Vector2i pos, out RGBColor col);
    public RGBColor getPixelColor(Vector2i pos) {
        RGBColor ret;
        getPixelColor(pos,ret);
        return ret;
    }

    ///clear whole canvas to color col
    public abstract void clearCanvas(RGBColor col);

    ///fill a rectangle (right/bottom border excluded)
    public void fillRect(Vector2i p1, Vector2i p2, RGBColor col) {
        //oh well, could need optimization :-)
        for (int y = p1.y; y < p2.y; y++) {
            for (int x = p1.x; x < p2.x; x++) {
                drawSafePixel(Vector2i(x, y), col);
            }
        }
    }

    //non-overlapping block-copy of complete scanlines
    public void copyLine(int from, int to) {
        RGBColor col;
        for (int x = 0; x < mRes.x; x++) {
            getPixelColor(Vector2i(x, from), col);
            drawSafePixel(Vector2i(x, to), col);
        }
    }

    ///change size of canvas to newRes
    public abstract void resizeCanvas(Vector2i newRes);

    public void saveImageFile(ILenum type, char[] filename) {
        //always overwrite
        ilEnable(IL_FILE_OVERWRITE);

        writefln("Saving %dx%d image to %s",mRes.x,mRes.y,filename);

        ILuint imgName;
        ilGenImages(1, &imgName);
        ilBindImage(imgName);
        scope(exit)ilDeleteImages(1, &imgName);

        if (!ilTexImage(mRes.x, mRes.y, 1, 3, IL_RGB, IL_FLOAT, null)) {
            throw new Exception("Failed to init buffer for image " ~ filename);
        }

        //canvas uses screen coordinates
        ilRegisterOrigin(IL_ORIGIN_UPPER_LEFT);

        RGBColor[] imgBuf = new RGBColor[mRes.x * mRes.y];

        for (int y = 0; y < mRes.y; y++) {
            for (int x = 0; x < mRes.x; x++) {
                getPixelColor(Vector2i(x,y), imgBuf[y*mRes.x+x]);
            }
        }
        ilSetPixels(0, 0, 0, mRes.x, mRes.y, 1, IL_RGB, IL_FLOAT, imgBuf.ptr);

        ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE);

        if (!ilSave(type, toStringz(filename))) {
            throw new Exception("Failed to write image file " ~ filename ~
                " : " ~ std.string.toString(ilGetError()));
        }
    }

    public void savePNG(char[] filename) {
        saveImageFile(IL_PNG,filename);
    }

    public void saveHDR(char[] filename) {
        //DevIL does not (yet) support saving hdr
        throw new Exception("Implement");
    }

    public int width() {
        return mRes.x;
    }

    public int height() {
        return mRes.y;
    }

    public Vector2i size() {
        return mRes;
    }

    public Time deltaT() {
        return mDeltaT;
    }

}
